﻿using System;
using System.Collections;
using System.ComponentModel;
using System.IO;
using System.Xml.Linq;
using Microsoft.Win32;

namespace AssemblyInfoDialog.Installer.Code
{
    [RunInstaller(true)]
    public partial class CustomInstaller : System.Configuration.Install.Installer
    {
        private const string AddInSavePath = "addInSavePath";
        private const string ProductExtensionDirectory = "productExtensionDirectory";

        public CustomInstaller()
        {
            InitializeComponent();
        }

        public override void Install(IDictionary savedState)
        {
            base.Install(savedState);

            //Debugger.Launch();

            DirectoryInfo installationDirectory = CheckGetInstallationDirectory();

            HandleAddInFile(savedState, installationDirectory);
            HandleVsixFile(savedState, installationDirectory);
        }

        public override void Rollback(IDictionary savedState)
        {
            base.Rollback(savedState);

            //Debugger.Launch();

            Cleanup(savedState);
        }

        public override void Uninstall(IDictionary savedState)
        {
            base.Uninstall(savedState);

            //Debugger.Launch();

            Cleanup(savedState);
        }

        #region Helpers

        private void HandleVsixFile(IDictionary savedState, DirectoryInfo installationDirectory)
        {
            var vsixFile = installationDirectory.GetFiles("*.vsixmanifest", SearchOption.TopDirectoryOnly);

            if (vsixFile.Length == 0)
                throw new InvalidOperationException(
                    String.Format("No .vsixmanifest was file found at '{0}'.", installationDirectory.FullName));

            var document = XDocument.Load(vsixFile[0].FullName);
            XNamespace ns = "http://schemas.microsoft.com/developer/vsx-schema/2010";
            var version = document.Root.Element(ns + "Identifier").Element(ns + "Version").Value;

            DirectoryInfo environmentDirectory;
            if (!TryGetVisualStudioEnvironmentDirectory(out environmentDirectory))
                throw new InvalidOperationException("Visual Studio 2010 not installed.");

            DirectoryInfo productExtensionDirectory =
                environmentDirectory.CreateSubdirectory(
                    String.Format(
                        @"Extensions\{0}\{1}\",
                        Context.Parameters["M"],
                        Context.Parameters["P"]));

            savedState.Add(ProductExtensionDirectory, productExtensionDirectory.FullName);

            DirectoryInfo versionDirectory =
                productExtensionDirectory.CreateSubdirectory(version);

            vsixFile[0].CopyTo(Path.Combine(versionDirectory.FullName, vsixFile[0].Name));
        }

        private static void HandleAddInFile(IDictionary savedState, DirectoryInfo installationDirectory)
        {
            var addInFile = installationDirectory.GetFiles("*.addin", SearchOption.TopDirectoryOnly);

            if (addInFile.Length == 0)
                throw new InvalidOperationException(
                    String.Format("No .addin file was found at '{0}'.", installationDirectory.FullName));

            var document = XDocument.Load(addInFile[0].FullName);
            XNamespace ns = "http://schemas.microsoft.com/AutomationExtensibility";
            var assembly = document.Root.Element(ns + "Addin").Element(ns + "Assembly");
            assembly.Value = Path.Combine(installationDirectory.FullName, assembly.Value);

            string addInSavePath = Path.Combine(CheckGetVisualStudioAddInsDirectory().FullName, addInFile[0].Name);

            document.Save(addInSavePath);
            savedState.Add(AddInSavePath, addInSavePath);
        }

        private DirectoryInfo CheckGetInstallationDirectory()
        {
            string installationDirectory = Context.Parameters["AssemblyPath"];

            if (String.IsNullOrEmpty(installationDirectory))
                throw new InvalidOperationException("Unable to get value from Parameter 'AssemblyPath'");

            installationDirectory = Path.GetDirectoryName(installationDirectory);

            if (String.IsNullOrEmpty(installationDirectory))
                throw new InvalidOperationException(String.Format("Unable to get directory from value '{0}'", installationDirectory));

            if (!Directory.Exists(installationDirectory))
                throw new InvalidOperationException(String.Format("Directory '{0}' does not exist.", installationDirectory));

            return new DirectoryInfo(installationDirectory);
        }

        private static DirectoryInfo CheckGetVisualStudioAddInsDirectory()
        {
            string myDocuments =
                Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

            if (!Directory.Exists(myDocuments))
                throw new InvalidOperationException(
                    String.Format("My Documents folder at '{0}' was not found.", myDocuments));

            return new DirectoryInfo(myDocuments).CreateSubdirectory(@"Visual Studio 2010\Addins");
        }

        private static bool TryGetVisualStudioEnvironmentDirectory(out DirectoryInfo environmentDirectory)
        {
            environmentDirectory = null;
            string directory = null;

            try
            {
                var registryKey =
                    Registry.LocalMachine
                        .OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\10.0\Setup\VS");

                if (registryKey != null)
                    directory = registryKey.GetValue("EnvironmentDirectory").ToString();
            }
            catch
            {
                return false;
            }

            if (String.IsNullOrEmpty(directory) || !Directory.Exists(directory))
                return false;

            environmentDirectory = new DirectoryInfo(directory);
            return true;
        }

        private static void Cleanup(IDictionary savedState)
        {
            var addInSavePath = (string) savedState[AddInSavePath];

            if (!String.IsNullOrEmpty(addInSavePath) && File.Exists(addInSavePath))
            {
                try
                {
                    File.Delete(addInSavePath);
                }
                catch
                {
                }
            }

            var extensionsDirectory = (string) savedState[ProductExtensionDirectory];

            if (!String.IsNullOrEmpty(extensionsDirectory) && Directory.Exists(extensionsDirectory))
            {
                try
                {
                    Directory.Delete(extensionsDirectory, true /* recursive */);
                }
                catch
                {
                }
            }
        }

        #endregion
    }
}
